Skip to content

direct: Fix "Nothing to update" when a UC comment is cleared or set outside the bundle - #6343

Open
denik wants to merge 16 commits into
mainfrom
denik/issue-6340
Open

direct: Fix "Nothing to update" when a UC comment is cleared or set outside the bundle#6343
denik wants to merge 16 commits into
mainfrom
denik/issue-6340

Conversation

@denik

@denik denik commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Why

A schema that does not declare comment becomes undeployable as soon as someone
sets a description on it in UC. The plan clears the comment, every UpdateSchema
field is omitempty, and the empty PATCH is rejected:

Error: cannot update resources.schemas.repro_schema: updating id=main.repro_schema: UpdateSchema Nothing to update. (400 INVALID_PARAMETER_VALUE)

Catalogs and volumes fail the same way. Registered models and external locations
always send other fields, so there is nothing to reject — instead removing a
comment from the configuration reports "1 changed" and never converges.

Changes

Each of the five resources names the fields its update force-sends, so a value the
config stops declaring is sent as the zero value instead of being dropped. Only
comment qualifies: the backend has to accept the zero value as a clear (owner: ""
and new_name: "" are both rejected), ForceSendFields has to affect the field at
all (it is inert for maps, so properties cannot be cleared by any payload), and
terraform has to send it too (custom_max_retention_hours fails that one).

backend_defaults would be wrong here: a description typed in Catalog Explorer is
real drift, not a value the backend filled in, and suppressing it would stop the
bundle from managing comment.

Fixes #6340

This pull request and its description were written by Isaac.

@eng-dev-ecosystem-bot

eng-dev-ecosystem-bot commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: 35cab36

Run: 32861749369

Env 💚​RECOVERED 🙈​SKIP ✅​pass 🙈​skip Time
💚​ aws linux 1 4 284 1184 6:53
💚​ aws windows 1 4 286 1182 5:44
💚​ azure linux 1 4 283 1184 7:28
💚​ azure windows 1 4 285 1182 5:18
💚​ gcp linux 1 4 284 1184 7:55
💚​ gcp windows 1 4 286 1182 4:44
Test Name aws linux aws windows azure linux azure windows gcp linux gcp windows
💚​ TestAccept 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
🙈​ TestAccept/bundle/invariant/no_drift 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/vector_search_endpoints/drift/recreated_same_name 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/vector_search_indexes/recreate/embedding_dimension 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/ssh/connection 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
Top 3 slowest tests (at least 2 minutes):
duration env testname
3:16 azure windows TestAccept
3:09 aws windows TestAccept
3:01 gcp windows TestAccept

@denik denik changed the title Fix "UpdateSchema Nothing to update" when a schema comment is set outside the bundle Fix "Nothing to update" when a UC comment is set outside the bundle Aug 22, 2026
@denik
denik force-pushed the denik/issue-6340 branch from e9bd2fc to 02ee8cd Compare August 24, 2026 12:19
denik added a commit that referenced this pull request Aug 24, 2026
## Why
We already pass it to DoUpdate, so it was just an omission.

Planned to be used in #6343
@denik
denik force-pushed the denik/issue-6340 branch 3 times, most recently from 0596606 to 1457ca6 Compare August 25, 2026 07:29
@denik denik changed the title Fix "Nothing to update" when a UC comment is set outside the bundle direct: Fix "Nothing to update" when a UC comment is cleared or set outside the bundle Aug 25, 2026
denik added 14 commits August 25, 2026 14:08
…he bundle

A schema without `comment` in the config becomes undeployable once someone sets
a description on it in UC: the engine reads the remote comment, plans an update,
and every field of the PATCH serializes away under omitempty. UC rejects the
empty body with `400 INVALID_PARAMETER_VALUE / UpdateSchema Nothing to update`,
which aborts the whole deploy.

Add an acceptance test for it and make the fake workspace reject an empty
UpdateSchema payload the way UC does, so the local run fails the same way the
cloud one does.

Co-authored-by: Isaac
…n empty PATCH

Every UpdateSchema field is omitempty, so a schema whose config declares no
comment produced an empty PATCH body once the comment was set out of band. UC
answers that with `400 / UpdateSchema Nothing to update` rather than a no-op,
which failed the whole deploy with no way out from the CLI.

Force-send comment so the payload always carries a field and clearing a comment
set outside the bundle actually happens.

Co-authored-by: Isaac
Catalogs and volumes fail exactly like schemas did: their update payloads carry
only fields the config may leave unset, so clearing a comment that was set out of
band produced an empty PATCH and `400 / Nothing to update`. Verified against a
real workspace for both.

Force-send comment in all four update paths (catalogs and volumes each have a
rename variant), moving the shared reason into forceSendComment, and teach the
fake workspace to reject an empty payload and honour an explicit empty comment
the way UC does.

Co-authored-by: Isaac
The bundle name is the workspace state path, and cloud tests share one real
workspace, so the hardcoded "test-bundle" made these three fight over the same
deploy.lock as every other test using that name. They passed run alone and
failed under parallelism: the integration run reported success while retrying
them on nearly every environment.

Co-authored-by: Isaac
…itionally

The previous commits always force-sent comment, so a deploy whose only drift was
another field still put `"comment": ""` on the wire, and a field the plan had
classified as skip would have been cleared behind the plan's back. Any other
omitempty field the config stops setting was still dropped, so it never
converged.

Derive the force-send list from the plan instead, matched against the request
type's own JSON names. Each PATCH now carries exactly what the plan says is
changing, and roughly 40 omitempty fields across the UC resources converge
instead of only comment. forceSendClearedFields documents the general shape,
including why full-replacement APIs (jobs, pipelines, model serving) need none
of it.

Also drop the special-casing in the fake workspace: applyUpdatedFields applies
whatever the payload names, including zero values, which is what a partial-update
API does. That incidentally stops mergo from clobbering the stored
ForceSendFields, so browse_only survives an update the way UC returns it.

Co-authored-by: Isaac
… locations

Removing a comment from the configuration reports a successful update and then
never converges: the field is omitempty, so it leaves the payload entirely, and
a partial-update API reads its absence as "leave unchanged". The comment stays,
and every later plan reports the same pending change.

Unlike the schema case this needs no out-of-band edit, and there is no error to
notice -- the deploy says "1 changed" while changing nothing.

The external locations golden also gains four synced files, since the new test
lives inside that test's bundle root.

Co-authored-by: Isaac
… too

Apply forceSendClearedFields to the two remaining resources whose update drops a
cleared field, so removing a comment from the configuration converges instead of
reporting a change forever. External locations get it on both the update and the
rename path.

The fake workspace applies whatever an update payload names, replacing guards that
swallowed an explicit empty comment. Registered models accept a payload with no
field at all -- unlike schemas, catalogs and volumes, verified against a real
workspace -- so parseUCUpdate is split and they use the parse-only half rather than
gaining a rejection the backend does not have.

Co-authored-by: Isaac
main removed the setting in #6359 while this branch was open, so the merge failed
with "Undecoded key ... RequiresUnityCatalog" on the direct-engine runners.

Co-authored-by: Isaac
resources/<resource>/<test-name> matches the equivalent tests that already live
flat (cluster_policies/out_of_band_change, apps/config-drift,
permissions/jobs/added_remotely), and the drift prefix says nothing the names do
not. Pure rename, no content change.

Co-authored-by: Isaac
grep -v silently leaves the config unchanged if the pattern stops matching, and
these tests would then assert convergence against a config that still declares
the comment. update_file.py fails instead, and the intermediate copies of
databricks.yml are no longer needed.

Co-authored-by: Isaac
External locations already did this; catalogs and volumes did not, so a deploy
that renamed one of them dropped a field the plan reported as cleared. The stale
comment left behind claimed DoUpdateWithID cannot see the plan, which stopped
being true when #6360 landed.

Co-authored-by: Isaac
An adversarial review found that removing a config-set owner from a registered
model made the plan report an actionable change, so the cleared value was
force-sent as owner: "" -- which UC rejects with "Could not find principal with
name .". backend_defaults does not cover it: that rule only skips when old and
new are both nil.

Route the cleared fields through the same FilterFields exclusions as
config.ForceSendFields, so the exclusion list each request already declares is
authoritative for both. The comment claiming owner is not in the configuration
tree was wrong: it is, via the embedded CreateRegisteredModelRequest.

Also record the whole changes map in the drift goldens rather than just comment,
and stop the fake workspace accumulating duplicate ForceSendFields entries.

Co-authored-by: Isaac
Deriving from entry.Changes made every omitempty field a candidate, so the
default for an unverified field was to send its zero value -- the hazard #6088
was about ('' is not a valid cluster policy ID). It also went against
dresources/README.md, which asks for a static list rather than field names
derived from the plan.

Each resource now names the fields it force-sends, and only comment qualifies.
Three things have to hold, and the last one is easy to miss: the backend must
accept the zero value as a clear (rules out owner and new_name), ForceSendFields
must affect the field at all (rules out maps such as properties), and terraform
must send it too (rules out custom_max_retention_hours, which UC does clear on 0
but terraform never sends, so force-sending it made the engines disagree).

Co-authored-by: Isaac
@denik
denik force-pushed the denik/issue-6340 branch from 408bc92 to 8fb90d2 Compare August 25, 2026 14:01
denik added 2 commits August 25, 2026 16:07
Removing the retention section left the file ending in a blank line, which the
whitespace linter fixes and CI then fails on.

Co-authored-by: Isaac
Recording the whole changes map surfaced the backend-generated storage_location,
whose scheme and bucket differ per cloud, so the azure and gcp integration runs
failed on an s3:// golden. Normalize to the AWS form ahead of the METASTORE_NAME
repl, the same way grants/volumes does. Verified against azure and gcp.

ForceSendFields is now assigned inline: the two-step only existed because the old
reflection helper had to inspect the built request. print_requests.py moves out of
the cleanup trap to just after the deploys, with the trap consuming the destroy's
requests so none are left recorded.

Co-authored-by: Isaac
@denik
denik marked this pull request as ready for review August 25, 2026 18:54
@github-actions

Copy link
Copy Markdown
Contributor

Approval status: pending

/acceptance/bundle/ - needs approval

31 files changed
Suggested: @pietern
Also eligible: @janniklasrose, @shreyas-goenka, @andrewnester, @lennartkats-db, @anton-107

/bundle/ - needs approval

7 files changed
Suggested: @pietern
Also eligible: @janniklasrose, @shreyas-goenka, @andrewnester, @lennartkats-db, @anton-107

General files (require maintainer)

7 files changed
Based on git history:

  • @pietern -- recent work in bundle/direct/dresources/, libs/testserver/

Any maintainer (@andrewnester, @anton-107, @pietern, @shreyas-goenka, @simonfaltum, @renaudhartert-db, @janniklasrose, @lennartkats-db, @rugpanov, @rclarey) can approve all areas.
See OWNERS for ownership rules.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

direct engine hard-fails with UpdateSchema Nothing to update when a schema comment is set outside the bundl

2 participants